package com.nowcoder.community.service;

import com.nowcoder.community.entity.User;
import com.nowcoder.community.util.CommunityConstant;
import com.nowcoder.community.util.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @BelongsProject: community
 * @BelongsPackage: com.nowcoder.community.service
 * @Author: Master_XXJS
 * @CreateTime: 2023-01-05  14:16
 * @Description: TODO
 * @Version: 1.0
 */
@Service
public class FollowService implements CommunityConstant {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserService userService;

    /**
     * 关注
     * @param userId 用户的id
     * @param entityType 实体的类型 用户、帖子、题目
     * @param entityId 实体id
     */
    public void follow(int userId,int entityType,int entityId){
        //redis 的事务管理
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations redisOperations) throws DataAccessException {
                //获取 redis 的key
                //实体拥有的粉丝
                String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
                //用户关注的实体（总的）
                String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);

                //开启事务
                redisOperations.multi();

                //关注之后，将数据 存入到 redis 中
                //这个 时间不算是 value。value 是 中间的参数
                //这个 时间（第3歌参数），就是转换为 score （double）的，进行排序，我们获取的时候需要转换为 long 类型才是时间
                redisOperations.opsForZSet().add(followeeKey,entityId,System.currentTimeMillis());
                redisOperations.opsForZSet().add(followerKey,userId,System.currentTimeMillis());

                return redisOperations.exec();
            }
        });
    }

    /**
     * 取消关注
     * @param userId 用户的id
     * @param entityType 实体的类型 用户、帖子、题目
     * @param entityId 实体id
     */
    public void unfollow(int userId,int entityType,int entityId){
        //redis 的事务管理
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations redisOperations) throws DataAccessException {
                //获取 redis 的key
                //实体拥有的粉丝
                String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
                //用户关注的实体（总的）
                String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);

                //开启事务
                redisOperations.multi();

                //关注之后，将数据 存入到 redis 中
                redisOperations.opsForZSet().remove(followeeKey,entityId);
                redisOperations.opsForZSet().remove(followerKey,userId);

                return redisOperations.exec();
            }
        });
    }

    /**
     * 查询用户关注的实体的数量
     * @param userId 用户id
     * @param entityType 实体类型
     * @return
     */
    public long findFolloweeCount(int userId,int entityType){
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
        return redisTemplate.opsForZSet().zCard(followeeKey);
    }

    /**
     * 查询实体的粉丝的数量
     * @param entityType 实体类型
     * @param entityId 实体id
     * @return
     */
    public long findFollowerCount(int entityType,int entityId){
        String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
        return redisTemplate.opsForZSet().zCard(followerKey);
    }

    /**
     * 查询当前用户是否是 查看的实体的粉丝，是的话，显示已关注
     * @param userId 当前的用户id
     * @param entityType 实体类型
     * @param entityId 实体id
     * @return
     */
    public boolean hasFollowed(int userId,int entityType,int entityId){
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);

        //查看  用户的关注的 实体里面，有没有 这个 实体 id
        //有的话，返回数值，没有返回 null
        System.err.println(followeeKey);
        System.err.println(entityId);
        return redisTemplate.opsForZSet().score(followeeKey, entityId) != null;
     }

    // TODO: 2023/1/6 这里可以自己写一个抽象类的方法，关注实体可以是用户，帖子，题目，话题 ，为了简单，我只写了 实体是用户的
    /**
     * 查看用户的 关注 的列表
     * @param userId 被查看的用户的id
     * @param offset 偏移量
     * @param limit 每页显示数量
     * @return
     */
     public List<Map<String,Object>> findFollowees(int userId,int offset,int limit){
         String followeeKey = RedisKeyUtil.getFolloweeKey(userId, ENTITY_TYPE_USER);
         //根据id进行查询 关注的用户的id，只查询一页的
         //为什么不是 -1，因为-1就是查询的全部了
         //offset+limit-1 刚好就是这个偏移量的那一页的显示数量
         Set<Integer> targetIds= redisTemplate.opsForZSet().reverseRange(followeeKey, offset, offset + limit - 1);

         if (targetIds == null){
             return null;
         }

         //定义集合，返回数据，user对象，关注的时间
         List<Map<String,Object>> list = new ArrayList<>();
         for (Integer targetId : targetIds){
             Map<String,Object> map = new HashMap<>();
             User user = userService.findUserById(targetId);
             map.put("user",user);
             //查询 这个存储的用户的 score ，就是我们之前存储的 时间
             //现在是 double类型，我们的时间是 long 类型，需要进行转换
             Double score = redisTemplate.opsForZSet().score(followeeKey, targetId);
             System.err.println("分数"+score);
             //将 double 转换为 long
             map.put("followTime",new Date(score.longValue()));
             System.err.println("转换之后的时间"+new Date(score.longValue()));
             list.add(map);
         }

         return list;
     }

    /**
     * 查看用户的 粉丝 的列表
     * @param userId 被查看的用户的id
     * @param offset 偏移量
     * @param limit 每页显示数量
     * @return
     */
    public List<Map<String,Object>> findFollowers(int userId,int offset,int limit){
        String followerKey = RedisKeyUtil.getFollowerKey(ENTITY_TYPE_USER,userId);
        //根据id进行查询 用户的粉丝的id，只查询一页的
        //为什么不是 -1，因为-1就是查询的全部了
        //offset+limit-1 刚好就是这个偏移量的那一页的显示数量
        Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followerKey, offset, offset + limit - 1);

        if (targetIds == null){
            return null;
        }

        //定义集合，返回数据，user对象，关注的时间
        List<Map<String,Object>> list = new ArrayList<>();
        for (Integer targetId : targetIds){
            Map<String,Object> map = new HashMap<>();
            User user = userService.findUserById(targetId);
            map.put("user",user);
            //查询 这个存储的用户的 score ，就是我们之前存储的 时间
            //现在是 double类型，我们的时间是 long 类型，需要进行转换
            Double score = redisTemplate.opsForZSet().score(followerKey, targetId);
            //将 double 转换为 long
            map.put("followTime",new Date(score.longValue()));
            list.add(map);
        }

        return list;
    }
}

